Coverage Report

Created: 2021-08-28 18:14

D:\git\skunkworks\herald-for-cpp\herald\include\herald\datatype\randomness.h
Line
Count
Source (jump to first uncovered line)
1
//  Copyright 2020-2021 Herald Project Contributors
2
//  SPDX-License-Identifier: Apache-2.0
3
//
4
5
#ifndef HERALD_RANDOMNESS_H
6
#define HERALD_RANDOMNESS_H
7
8
#include "data.h"
9
10
#include <string>
11
#include <random>
12
#include <climits>
13
14
namespace herald {
15
namespace datatype {
16
17
/**
18
 * A Randomness Source provides random data. It could be a simple
19
 * uniform integer distribution, it could be a predictable sequence,
20
 * it could be derived from a chipsets secure enclave, if present.
21
 * 
22
 * This high level class abstracts away the underlying implementation
23
 * mechanics. On native platforms in C++ the implementations available may
24
 * vary dramatically.
25
 */
26
// class RandomnessSource {
27
// public:
28
//   RandomnessSource() = default;
29
//   virtual ~RandomnessSource() = default;
30
31
//   virtual std::string methodName() const = 0;
32
33
//   virtual void nextBytes(std::size_t count, Data& into) = 0;
34
//   virtual int nextInt() = 0;
35
//   virtual double nextDouble() = 0;
36
37
// };
38
39
/**
40
 * A decidedly non random source!!! Used to test the v4 UUID format generation function only.
41
 * DO NOT USE IN PRODUCTION.
42
 */
43
class AllZerosNotRandom {
44
public:
45
  AllZerosNotRandom() = default;
46
  AllZerosNotRandom(AllZerosNotRandom&& other) noexcept = default;
47
  ~AllZerosNotRandom() = default;
48
49
0
  std::string methodName() const {
50
0
    return "allzeros";
51
0
  }
52
53
2
  void nextBytes(std::size_t count, Data& into) {
54
22
    for (std::size_t i = 0;i < count;
i++20
) {
55
20
      into.append(std::byte(0));
56
20
    }
57
2
  }
58
59
1
  int nextInt() {
60
1
    return 0;
61
1
  }
62
63
1
  double nextDouble() {
64
1
    return 0.0;
65
1
  }
66
};
67
68
class IntegerDistributedRandomSource {
69
public:
70
  IntegerDistributedRandomSource() 
71
    : rd(), gen(rd()), distrib(LONG_MIN,LONG_MAX)
72
3
  {
73
3
    ;
74
3
  }
75
76
  IntegerDistributedRandomSource(IntegerDistributedRandomSource&& other) noexcept
77
    : rd(), // Doesn't have a move or copy constructor
78
      gen(rd()),
79
      distrib(other.distrib)
80
3
  {
81
3
    ;
82
3
  } 
83
84
  ~IntegerDistributedRandomSource() = default;
85
86
0
  std::string methodName() const {
87
0
    return "integerdistributed";
88
0
  }
89
90
4
  void nextBytes(std::size_t count, Data& into) {
91
68
    for (std::size_t i = 0;i < count;
i++64
) {
92
64
      into.append(std::byte(distrib(gen)));
93
64
    }
94
4
  }
95
96
0
  int nextInt() {
97
0
    return (int)distrib(gen);
98
0
  }
99
100
0
  double nextDouble() {
101
0
    return (double)distrib(gen);
102
0
  }
103
104
private:
105
  std::random_device rd;  // Will be used to obtain a seed for the random number engine
106
  std::mt19937 gen; // Standard mersenne_twister_engine seeded with rd()
107
  std::uniform_int_distribution<int64_t> distrib;
108
};
109
110
/**
111
 * The Randomness Generator IS A source of randomness, but may also
112
 * be used by the application to inject a secondary source of entropy
113
 * in to the result of the underlying RandomnessSource implementation.
114
 * 
115
 * This is the class used by Herald. This allows application developers
116
 * to use the most appropriate randomness source for their target
117
 * platform and application needs.
118
 * 
119
 * A secondary source of entropy may be something else going on in the
120
 * app. In Herald, for example, it could be the time it actually takes
121
 * to complete a scan-and-interact loop. This combines outside effects
122
 * of communication, internal timer/interrupt timing changes, and
123
 * internal state computation times to make the entropy unpredictable.
124
 */
125
template <typename RandomnessSourceT>
126
class RandomnessGenerator {
127
public:
128
  RandomnessGenerator(RandomnessSourceT&& toOwn)
129
    : m_source(std::move(toOwn)),
130
      m_entropy(0)
131
4
  {
132
4
    ;
133
4
  }
??0?$RandomnessGenerator@VAllZerosNotRandom@datatype@herald@@@datatype@herald@@QEAA@$$QEAVAllZerosNotRandom@12@@Z
Line
Count
Source
131
1
  {
132
1
    ;
133
1
  }
??0?$RandomnessGenerator@VIntegerDistributedRandomSource@datatype@herald@@@datatype@herald@@QEAA@$$QEAVIntegerDistributedRandomSource@12@@Z
Line
Count
Source
131
3
  {
132
3
    ;
133
3
  }
134
135
  ~RandomnessGenerator() = default;
136
137
  template <typename T>
138
  void addEntropy(T entropy) {
139
    // Get size of amount of entropy we have
140
    constexpr std::size_t size = sizeof(T);
141
    // TODO consider whether there's benefit to detecting most significant set bits.
142
    //      This may provide a benefit if the method of combination isn't XOR.
143
    
144
    // See if we are at multiples of std::size_t
145
    constexpr std::size_t multiple = sizeof(std::size_t) / size; // integer division, rounds down
146
    // Note the above cannot ever be 0 because std::size_t is always the largest size on a given architecture
147
148
    std::size_t toXor = 0;
149
    for (std::size_t i = 0;i < multiple;i++) {
150
      if (0 != i) {
151
        toXor << size;
152
      }
153
      toXor += entropy;
154
    }
155
156
    // XOR will ensure the same distribution of 0 and 1 over multiple applications
157
    m_entropy = m_entropy ^ toXor;
158
  }
159
160
  
161
  std::string methodName() const {
162
    return m_source.methodName();
163
  }
164
165
5
  void nextBytes(std::size_t count, Data& into) {
166
5
    constexpr std::size_t byteSize = sizeof(std::byte);
167
5
    constexpr std::size_t sizeTSize = sizeof(std::size_t);
168
5
    constexpr std::size_t shifts = sizeTSize / byteSize;
169
5
    Data sourcedInto;
170
5
    m_source.nextBytes(count,sourcedInto);
171
5
172
5
    // now add in entropy
173
85
    for (std::size_t byteIndex = 0;byteIndex < count;
byteIndex++80
) {
174
80
      into.append((std::byte)(
175
80
        std::size_t(sourcedInto.at(byteIndex))
176
80
        ^ 
177
80
        (m_entropy >> 8 * (byteIndex % shifts))
178
80
      ));
179
80
    }
180
5
  }
?nextBytes@?$RandomnessGenerator@VAllZerosNotRandom@datatype@herald@@@datatype@herald@@QEAAX_KAEAV?$DataRef@V?$MemoryArena@$0CAAA@$07@datatype@herald@@@23@@Z
Line
Count
Source
165
1
  void nextBytes(std::size_t count, Data& into) {
166
1
    constexpr std::size_t byteSize = sizeof(std::byte);
167
1
    constexpr std::size_t sizeTSize = sizeof(std::size_t);
168
1
    constexpr std::size_t shifts = sizeTSize / byteSize;
169
1
    Data sourcedInto;
170
1
    m_source.nextBytes(count,sourcedInto);
171
1
172
1
    // now add in entropy
173
17
    for (std::size_t byteIndex = 0;byteIndex < count;
byteIndex++16
) {
174
16
      into.append((std::byte)(
175
16
        std::size_t(sourcedInto.at(byteIndex))
176
16
        ^ 
177
16
        (m_entropy >> 8 * (byteIndex % shifts))
178
16
      ));
179
16
    }
180
1
  }
?nextBytes@?$RandomnessGenerator@VIntegerDistributedRandomSource@datatype@herald@@@datatype@herald@@QEAAX_KAEAV?$DataRef@V?$MemoryArena@$0CAAA@$07@datatype@herald@@@23@@Z
Line
Count
Source
165
4
  void nextBytes(std::size_t count, Data& into) {
166
4
    constexpr std::size_t byteSize = sizeof(std::byte);
167
4
    constexpr std::size_t sizeTSize = sizeof(std::size_t);
168
4
    constexpr std::size_t shifts = sizeTSize / byteSize;
169
4
    Data sourcedInto;
170
4
    m_source.nextBytes(count,sourcedInto);
171
4
172
4
    // now add in entropy
173
68
    for (std::size_t byteIndex = 0;byteIndex < count;
byteIndex++64
) {
174
64
      into.append((std::byte)(
175
64
        std::size_t(sourcedInto.at(byteIndex))
176
64
        ^ 
177
64
        (m_entropy >> 8 * (byteIndex % shifts))
178
64
      ));
179
64
    }
180
4
  }
181
182
  int nextInt() {
183
    return (int)(m_source.nextInt() ^ m_entropy);
184
  }
185
186
  double nextDouble() {
187
    return (double)(((std::size_t)m_source.nextDouble()) ^ m_entropy);
188
  }
189
190
private:
191
  RandomnessSourceT m_source;
192
  std::size_t m_entropy;
193
};
194
195
} // end namespace
196
} // end namespace
197
198
#endif